#include "stdafx.h"
#include "MaskMode.h"
#include <MyControls/EnumProperty.h>
#include <MyControls/StdProperties.h>
#include "Resource.h"
#include "read_enum.h"
#include <xml/stream.h>

// bit mask

void AddMask(BitMask & comp, BitMask const & mask, MaskMode mm, bool first_mask)
{
	if (first_mask) {
		assert (mm.mode != MaskMode::None);
		comp = mask;
		if (!mm.inverted && mm.mode == MaskMode::Subtract || mm.inverted && mm.mode != MaskMode::Subtract)
			comp.inverse();
		return;
	}

	switch (mm.mode) {
	case MaskMode::Substitute:
	default:
		assert (false);
	case MaskMode::None:
		break;
	case MaskMode::Add:
	case MaskMode::Lighten:
		if (!mm.inverted)
			img::combine(comp, mask, img::bit_or());
		else
			img::combine(comp, mask, img::bit_or_not());
		break;
	case MaskMode::Intersect:
	case MaskMode::Darken:
		if (!mm.inverted)
			img::combine(comp, mask, img::bit_and());
		else
			img::combine(comp, mask, img::bit_and_not());
		break;
	case MaskMode::Subtract:
		if (!mm.inverted)
			img::combine(comp, mask, img::bit_and_not());
		else
			img::combine(comp, mask, img::bit_and());
		break;
	case MaskMode::Difference:
		if (!mm.inverted)
			img::combine(comp, mask, img::bit_xor());
		else
			img::combine(comp, mask, img::bit_xor_not());
		break;
	}
}

// grey mask

struct add {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = (r1 + r2 >= 255) ? 255 : r1 + r2; }
};
struct add_inv {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = (r1 >= r2) ? 255 : r1 + 255 - r2; }
};

struct subtract {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = (r1 <= r2) ? 0 : r1 - r2; }
};
struct subtract_inv {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = (r1 + r2 <= 255) ? 0 : r1 + r2 - 255; }
};

struct intersect1 {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = r1 * r2 / 255; }
};
struct intersect_inv {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = r1 * (255 - r2) / 255; }
};

struct lighten {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = std::max(r1, r2); }
};
struct lighten_inv {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = std::max<unsigned char>(r1, 255 - r2); }
};

struct darken {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = std::min(r1, r2); }
};
struct darken_inv {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = std::min<unsigned char>(r1, 255 - r2); }
};

struct difference {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = std::abs(r1 - r2); }
};
struct difference_inv {
	void operator () (unsigned char & r1, unsigned char const & r2) const
		{ r1 = std::abs(r1 + r2 - 255); }
};

void AddMask(GreyMask & comp, GreyMask const & mask, MaskMode mm, bool first_mask)
{
	if (first_mask) {
		assert (mm.mode != MaskMode::None);
		comp = mask;
		if (!mm.inverted && mm.mode == MaskMode::Subtract || mm.inverted && mm.mode != MaskMode::Subtract) {
			//inverse comp
			for (GreyMask::iterator i = comp.begin(); i != comp.end(); ++i)
				*i = 255 - *i;
		}
		return;
	}

	switch (mm.mode) {
	case MaskMode::Substitute:
	default:
		assert (false);
	case MaskMode::None:
		break;
	case MaskMode::Add:
		if (!mm.inverted)
			img::transform(comp, mask, add());
		else
			img::transform(comp, mask, add_inv());
		break;
	case MaskMode::Subtract:
		if (!mm.inverted)
			img::transform(comp, mask, subtract());
		else
			img::transform(comp, mask, subtract_inv());
		break;
	case MaskMode::Intersect:
		if (!mm.inverted)
			img::transform(comp, mask, intersect1());
		else
			img::transform(comp, mask, intersect_inv());
		break;
	case MaskMode::Lighten:
		if (!mm.inverted)
			img::transform(comp, mask, lighten());
		else
			img::transform(comp, mask, lighten_inv());
		break;
	case MaskMode::Darken:
		if (!mm.inverted)
			img::transform(comp, mask, darken());
		else
			img::transform(comp, mask, darken_inv());
		break;
	case MaskMode::Difference:
		if (!mm.inverted)
			img::transform(comp, mask, difference());
		else
			img::transform(comp, mask, difference_inv());
		break;
	}
}

// xml

static const char * mm_str[] = 
	{"None", "Add", "Subtract", "Intersect", "Lighten", "Darken", "Difference", "Substitute", 0};
DEFINE_INLINE_ENUM_IO_OPERATORS(MaskMode::Mode, mm_str);

xml::stream & operator || (xml::stream & s, MaskMode & mm)
{
	using namespace xml;
	return s || def_in_attr("mode", mm.mode, MaskMode::Add) || def_in_attr("inverted", mm.inverted, false);
}

// property for the editor

template <>
My::EnumItem<MaskMode::Mode> My::EnumTypeControl<MaskMode::Mode>::Items[] =
{
	{"None", MaskMode::None},
	{"Add", MaskMode::Add},
	{"Subtract", MaskMode::Subtract},
	{"Intersect", MaskMode::Intersect},
	{"Lighten", MaskMode::Lighten},
	{"Darken", MaskMode::Darken},
	{"Difference", MaskMode::Difference},
	{"Substitute", MaskMode::Substitute}
};
SPECIALIZE_ENUM_TYPE_CONTROL(MaskMode::Mode)

My::Property<MaskMode>::Property() :
	StructProperty<MaskMode>(true)
{
}

void My::Property<MaskMode>::Init()
{
	(* this)[IDS_SEGMENT_MASK_MODE] = &Create(&MaskMode::mode).Hint(IDS_SEGMENT_MASK_MODE_HINT);
	(* this)[IDS_SEGMENT_MASK_INVERTED] = &Create(&MaskMode::inverted).Hint(IDS_SEGMENT_MASK_INVERTED_HINT);
}
